// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "connection.h"

#include <event2/bufferevent.h>
#include "glog/logging.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"

namespace seven_summits {

bool called_init = false;
void init_logging() {
  if (!called_init) {
    google::InitGoogleLogging("foobar");
    called_init = true;
  }
}
const char kSampleQuery[] = {
  "(query [(Eq w8 (Read w8 0 mem) 10)] false [(w8 0)] [])"
};

using testing::_;
using testing::Invoke;
using testing::Return;
using testing::SetArgPointee;

class ConnectionTest : public ::testing::Test {
 public:
  class MockBufferEvent : public Connection::BufferEvent {
   public:
    MockBufferEvent() : Connection::BufferEvent() {}
    virtual ~MockBufferEvent() {}
    MOCK_METHOD1(ReceivedMoreThan, bool(size_t));
    MOCK_METHOD2(Read, size_t(void*, size_t));
    MOCK_METHOD2(Write, void(const void*, size_t));
  };

  void CheckNumRequestBytes() {
    EXPECT_EQ(request_.ByteSize(), connection_->num_request_bytes());
  }

  void ParseRequest() {
    connection_->ParseRequest();
  }

  void HandleRead() {
    connection_->HandleRead();
  }
  size_t HandleReadRequestSize(void* dest, size_t num_bytes);
  size_t HandleReadMessage(void* dest, size_t num_bytes);

 protected:
  ConnectionTest()
      : mock_buffer_event_(new MockBufferEvent()),
      connection_(new Connection(mock_buffer_event_)) {
    init_logging();
    request_.set_query(kSampleQuery);
  }
  virtual ~ConnectionTest() {
    delete connection_;
  };

  MockBufferEvent* mock_buffer_event_;
  Connection* connection_;
  SmtRequest request_;
};

size_t ConnectionTest::HandleReadRequestSize(void* dest, size_t num_bytes) {
  *((uint32_t*) dest) = request_.ByteSize();
  return num_bytes;
}

size_t ConnectionTest::HandleReadMessage(void* dest, size_t num_bytes) {
  std::stringstream ss(std::stringstream::in | std::stringstream::out);
  request_.SerializeToOstream(&ss);
  memcpy(dest, ss.str().c_str(), request_.ByteSize());

  return request_.ByteSize();
}

TEST_F(ConnectionTest, HandleReadSize) {
  EXPECT_CALL(*mock_buffer_event_, ReceivedMoreThan(sizeof(uint32_t)))
      .WillOnce(Return(true));
  EXPECT_CALL(*mock_buffer_event_, ReceivedMoreThan(request_.ByteSize()))
      .WillOnce(Return(false));
  EXPECT_CALL(*mock_buffer_event_, Read(_, sizeof(uint32_t)))
      .WillOnce(Invoke(this, &ConnectionTest::HandleReadRequestSize));
  HandleRead();
  CheckNumRequestBytes();
}

TEST_F(ConnectionTest, HandleReadRequest) {
  EXPECT_CALL(*mock_buffer_event_, ReceivedMoreThan(sizeof(uint32_t)))
      .WillOnce(Return(true));
  EXPECT_CALL(*mock_buffer_event_, ReceivedMoreThan(request_.ByteSize()))
      .WillOnce(Return(false));
  EXPECT_CALL(*mock_buffer_event_, Read(_, sizeof(uint32_t)))
      .WillOnce(Invoke(this, &ConnectionTest::HandleReadRequestSize));
  HandleRead();
  CheckNumRequestBytes();
}

TEST_F(ConnectionTest, ParseRequest) {
  EXPECT_CALL(*mock_buffer_event_, ReceivedMoreThan(sizeof(uint32_t)))
      .WillOnce(Return(true));
  EXPECT_CALL(*mock_buffer_event_, ReceivedMoreThan(request_.ByteSize()))
      .WillOnce(Return(true));
  EXPECT_CALL(*mock_buffer_event_, Read(_, sizeof(uint32_t)))
      .WillOnce(Invoke(this, &ConnectionTest::HandleReadRequestSize));
  EXPECT_CALL(*mock_buffer_event_, Read(_, request_.ByteSize()))
      .WillOnce(Invoke(this, &ConnectionTest::HandleReadMessage));
  HandleRead();
  CheckNumRequestBytes();
}

}  // namespace seven_summits
